<template>
    <div ref="aceEditor" :style="{ height: aceHeight }"></div>
</template>

<script lang="ts">
    import { defineComponent, ref, watch, nextTick, computed, onUnmounted, Ref } from "vue";
    import ace from "ace-builds";
    import "ace-builds/src-noconflict/mode-json";
    import "ace-builds/src-noconflict/theme-monokai";
    import "ace-builds/src-noconflict/ext-language_tools";
    import "ace-builds/src-noconflict/ext-emmet";
    import "ace-builds/src-noconflict/snippets/json";
    // import "ace-builds/src-noconflict/worker-json";
    export default defineComponent({
        name: "AceEditor",
        props: {
            // 数据
            modelValue: { type: String, default: () => "" },
            readOnly: { type: Boolean, default: () => false },
            // 外部传入的语法类型
            language: { type: String, default: () => "json" },
            // 编辑器主题色
            theme: { type: String, default: () => "monokai" },
            height: { type: String || Number },
            // 自动计算高度
            autoHeight: { type: Boolean, default: () => false },
        },
        emits: ["update:modelValue"],
        setup(props, { emit }) {
            let editor = ref<any>(null);
            const calcHeight = ref(0);
            // ace加载实例
            const aceEditor = ref(null) as Ref<HTMLDivElement>;
            let options = {
                theme: "ace/theme/" + props.theme, // 默认设置的主题
                mode: "ace/mode/" + props.language, // 默认设置的语言模式
                tabSize: 2, // 制表符设置为 2 个空格大小
                fontSize: 14, // 编辑器内字体大小
                showPrintMargin: true,
                readOnly: props.readOnly,
                useWorker: false,
                useWrapMode: true,
            };
            // 初始化实列
            const initialize = () => {
                if (editor.value) {
                    //实例销毁
                    editor.value.destroy();
                }
                editor.value = ace.edit(aceEditor.value, options);
                //代码提示和自动补全
                editor.value.setOptions({
                    enableSnippets: true,
                    enableLiveAutocompletion: true,
                    enableBasicAutocompletion: true,
                });
                editor.value.getSession().setUseWrapMode(true);
                // 支持双向绑定
                editor.value.on("change", () => {
                    if (emit) {
                        emit("update:modelValue", editor.value.getValue());
                    }
                });
                editor.value.getSession().setValue(props.modelValue);
            };
            // 获取数据
            const getValue = () => {
                return editor.value.getValue();
            };
            // 销毁实列
            const destroyEditor = async () => {
                await editor.value.destroy();
                if (emit) {
                    emit("update:modelValue", null);
                }
                editor.value = null;
            };
            const aceHeight = computed(() => {
                const { height } = props;
                if (props.autoHeight) {
                    return calcHeight.value ? calcHeight.value + "px" : "700px";
                }
                return height ? (typeof height == "number" ? height + "px" : height) : "700px";
            });
            watch(
                () => props.modelValue,
                (newProps: any) => {
                    if (editor.value) {
                        //解决双向数据绑定光标移动问题
                        const position = editor.value.getCursorPosition();
                        editor.value.getSession().setValue(newProps);
                        editor.value.clearSelection();
                        editor.value.moveCursorToPosition(position);
                    } else {
                        nextTick(() => {
                            initialize();
                        });
                    }
                    nextTick(() => {
                        // 计算高度
                        if (props.autoHeight) {
                            calcHeight.value = 0;
                            aceEditor.value.childNodes.forEach((item: any) => {
                                const className = item.className;
                                if (className.includes("ace_scroller")) {
                                    item.childNodes.forEach((childItem: any) => {
                                        const childClassName = childItem.className;
                                        if (childClassName.includes("ace_content")) {
                                            childItem.childNodes.forEach((contentItem: any) => {
                                                const contentClassName = contentItem.className;
                                                if (contentClassName.includes("ace_text-layer")) {
                                                    setTime = setTimeout(() => {
                                                        contentItem.childNodes.forEach((text: any) => {
                                                            calcHeight.value += text.clientHeight;
                                                        });
                                                    }, 10);
                                                }
                                            });
                                        }
                                    });
                                }
                            });
                        }
                    });
                },
                { immediate: true, deep: true }
            );
            let setTime: any;
            onUnmounted(() => {
                clearTimeout(setTime);
                setTime = null;
            });

            return {
                aceEditor,
                getValue,
                destroyEditor,
                aceHeight,
            };
        },
    });
</script>

<style scoped lang="scss"></style>
